/**
 * Copyright 2013 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2013-5-2 上午10:19:04
 */
package com.absir.android.view;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.app.Activity;
import android.app.Application;
import android.app.Dialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.PopupWindow;

import com.absir.android.view.annotation.InjectFlater;
import com.absir.android.view.annotation.InjectFlaters;
import com.absir.android.view.annotation.InjectLayer;
import com.absir.android.view.annotation.InjectMethod;
import com.absir.android.view.annotation.InjectView;
import com.absir.core.kernel.KernelClass;
import com.absir.core.kernel.KernelLang.BreakException;
import com.absir.core.kernel.KernelLang.CallbackBreak;
import com.absir.core.kernel.KernelLang.ObjectTemplate;
import com.absir.core.kernel.KernelObject;
import com.absir.core.kernel.KernelReflect;
import com.absir.core.kernel.KernelString;

/**
 * @author absir
 * 
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class InjectViewUtils {

	/** ID_NONE */
	public final static int ID_NONE = -1;

	/** ID_ZERO */
	public final static int ID_ZERO = 0;

	/** application */
	private static Application Application;

	/** R_Resouce_Class */
	private static Class<?> R_Resouce_Class = null;

	/** Type_Map_Resouce_Class */
	private static final Map<String, Object> Type_Map_Resouce_Class = new HashMap<String, Object>();

	/**
	 * @param application
	 */
	public static void setApplication(Application application) {
		Application = application;
		R_Resouce_Class = KernelClass.forName(application.getPackageName() + "." + "R");
	}

	/**
	 * @param type
	 * @param name
	 * @return
	 */
	public static Integer getResouceId(String type, String name) {
		Object resouce = Type_Map_Resouce_Class.get(type);
		if (resouce == null) {
			synchronized (type) {
				resouce = Type_Map_Resouce_Class.get(type);
				if (resouce == null) {
					resouce = KernelClass.forName(R_Resouce_Class.getName() + "$" + KernelString.uncapitalize(type));
				}

				if (resouce != null) {
					Type_Map_Resouce_Class.put(type, resouce);
				}
			}
		}

		return resouce == null ? null : (Integer) KernelObject.get(resouce, name);
	}

	/**
	 * @param id
	 * @param type
	 * @param name
	 * @return
	 */
	public static int getResouceId(int id, String type, String name) {
		if (id == ID_NONE) {
			Integer integer = getResouceId(type, name);
			id = integer == null ? ID_ZERO : integer;
		}

		return id;
	}

	/** Inject_Excutors */
	private static InjectExcutor[] Inject_Excutors = new InjectExcutor[] {

	new InjectExcutor<Activity>() {

		@Override
		public void setLayer(Activity object, int id) {
			// TODO Auto-generated method stub
			object.setContentView(id);
		}

		@Override
		public View loadView(Activity object, int id) {
			// TODO Auto-generated method stub
			return object.getLayoutInflater().inflate(id, null);
		}

		@Override
		public View findViewById(Activity object, int id) {
			// TODO Auto-generated method stub
			return object.findViewById(id);
		}
	},

	new InjectExcutor<View>() {

		@Override
		public void setLayer(View object, int id) {
			// TODO Auto-generated method stub

		}

		@Override
		public View loadView(View object, int id) {
			// TODO Auto-generated method stub
			return LayoutInflater.from(object.getContext()).inflate(id, null);
		}

		@Override
		public View findViewById(View object, int id) {
			// TODO Auto-generated method stub
			return object.findViewById(id);
		}

	},

	new InjectExcutor<Dialog>() {

		@Override
		public void setLayer(Dialog object, int id) {
			// TODO Auto-generated method stub
			// object.
		}

		@Override
		public View loadView(Dialog object, int id) {
			// TODO Auto-generated method stub
			return object.getLayoutInflater().inflate(id, null);
		}

		@Override
		public View findViewById(Dialog object, int id) {
			// TODO Auto-generated method stub
			return object.findViewById(id);
		}
	},

	new InjectExcutor<PopupWindow>() {

		@Override
		public void setLayer(PopupWindow object, int id) {
			// TODO Auto-generated method stub
			object.setContentView(LayoutInflater.from(Application).inflate(id, null));
		}

		@Override
		public View loadView(PopupWindow object, int id) {
			// TODO Auto-generated method stub
			View contentView = object.getContentView();
			return contentView == null ? null : LayoutInflater.from(contentView.getContext()).inflate(id, null);
		}

		@Override
		public View findViewById(PopupWindow object, int id) {
			// TODO Auto-generated method stub
			View contentView = object.getContentView();
			return contentView == null ? null : contentView.findViewById(id);
		}

	},

	};

	/**
	 * @param inject
	 * @return
	 */
	private static InjectExcutor forInjectObject(Object inject) {
		Class<?> type = inject.getClass();
		for (InjectExcutor<?> injectExcutor : Inject_Excutors) {
			if (injectExcutor.type.isAssignableFrom(type)) {
				return injectExcutor;
			}
		}

		return null;
	}

	/**
	 * @param object
	 */
	public static void inject(Object object) {
		inject(object, false);
	}

	/**
	 * @param object
	 * @param singleton
	 */
	public static void inject(Object object, boolean singleton) {
		InjectExcutor injectExcutor = forInjectObject(object);
		if (injectExcutor == null) {
			return;
		}

		InjectExcutorObject injectExcutorObject = new InjectExcutorObject(object, injectExcutor);
		List<InjectViewAbstract> injectViewAbstracts = singleton ? generateInjectAbstracts(injectExcutorObject)
				: getInjectAbstracts(injectExcutorObject);
		for (InjectViewAbstract injectViewAbstract : injectViewAbstracts) {
			injectViewAbstract.injectExcutor(injectExcutorObject);
		}
	}

	/**
	 * @param injectExcutorObject
	 * @return
	 */
	private static List<InjectViewAbstract> generateInjectAbstracts(InjectExcutorObject injectExcutorObject) {
		final Class<?> objectClass = injectExcutorObject.getObject().getClass();
		List<InjectViewAbstract> injectViewAbstracts = Inject_Class_Map_Inject_View_Abstracts.get(objectClass);
		if (injectViewAbstracts == null) {
			final ObjectTemplate<InjectViewLayer> injectLayerTemplate = new ObjectTemplate<InjectViewLayer>();
			final List<InjectViewAbstract> injectViewAbstractList = new ArrayList<InjectViewAbstract>();
			final List<InjectViewAbstract> injectViewAbstractScope = new ArrayList<InjectViewAbstract>();
			final List<InjectViewAbstract> injectViewFlaterList = new ArrayList<InjectViewAbstract>();
			final List<InjectViewAbstract> injectViewFlaterScope = new ArrayList<InjectViewAbstract>();
			final Set<Method> iMethodSet = new HashSet<Method>();
			KernelReflect.doWithClasses(objectClass, new CallbackBreak<Class<?>>() {

				@Override
				public void doWith(Class<?> template) throws BreakException {
					// TODO Auto-generated method stub
					if (injectLayerTemplate.object == null) {
						InjectLayer injectLayer = template.getAnnotation(InjectLayer.class);
						if (injectLayer != null) {
							injectLayerTemplate.object = new InjectViewLayer(injectLayer);
						}
					}

					InjectFlaters injectFlaters = template.getAnnotation(InjectFlaters.class);
					if (injectFlaters != null) {
						injectViewFlaterScope.add(0, new InjectViewFlaters(injectFlaters));
					}

					for (Field field : template.getDeclaredFields()) {
						if (!(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()))) {
							InjectView injectView = field.getAnnotation(InjectView.class);
							if (injectView != null) {
								injectViewAbstractScope.add(new InjectViewField(field, injectView));

							} else {
								InjectFlater injectFlater = field.getAnnotation(InjectFlater.class);
								if (injectFlater != null) {
									injectViewFlaterScope.add(new InjectViewFlater(field, injectFlater));
								}
							}
						}
					}

					for (Method method : template.getDeclaredMethods()) {
						InjectMethod injectMethod = method.getAnnotation(InjectMethod.class);
						if (injectMethod != null) {
							if (!(Modifier.isStatic(method.getModifiers()) || Modifier.isFinal(method.getModifiers()) || Modifier
									.isPrivate(method.getModifiers()))) {
								method = KernelReflect.declaredMethod(objectClass, method.getName(),
										method.getParameterTypes());
							}

							if (method != null && iMethodSet.add(method)) {
								injectViewAbstractScope.add(new InjectViewMethod(method, injectMethod));
							}
						}
					}

					injectViewAbstractList.addAll(0, injectViewAbstractScope);
					injectViewAbstractScope.clear();
					injectViewFlaterList.addAll(0, injectViewFlaterScope);
					injectViewFlaterScope.clear();
				}

			}, injectExcutorObject.getInjectExcutor().type);

			injectViewAbstractList.addAll(0, injectViewFlaterList);
			if (injectLayerTemplate.object != null) {
				injectViewAbstractList.add(0, injectLayerTemplate.object);
			}

			injectViewAbstracts = injectViewAbstractList;
		}

		return injectViewAbstracts;
	}

	/** Inject_Class_Map_Inject_View_Abstracts */
	private static Map<Class, List<InjectViewAbstract>> Inject_Class_Map_Inject_View_Abstracts = new HashMap<Class, List<InjectViewAbstract>>();

	/**
	 * @param injectExcutorObject
	 * @return
	 */
	private static List<InjectViewAbstract> getInjectAbstracts(InjectExcutorObject injectExcutorObject) {
		final Class<?> objectClass = injectExcutorObject.getObject().getClass();
		List<InjectViewAbstract> injectViewAbstracts = Inject_Class_Map_Inject_View_Abstracts.get(objectClass);
		if (injectViewAbstracts == null) {
			synchronized (objectClass) {
				injectViewAbstracts = generateInjectAbstracts(injectExcutorObject);
				Inject_Class_Map_Inject_View_Abstracts.put(objectClass, injectViewAbstracts);
			}
		}

		return injectViewAbstracts;
	}
}
